{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Chat Models大语言模型\n",
    "\n",
    "## 简介\n",
    "\n",
    "在Agent智能体中，大语言模型提供理解、决策、调度等能力，是最基础的大脑模块。因此，本文我们介绍如何上手使用大语言模型。\n",
    "\n",
    "简而言之，大语言模型基于深度学习技术，能够自动理解和生成人类自然语言文本的模型，广泛应用于文本生成、机器翻译、自动摘要、问答系统、情感分析等任务。\n",
    "\n",
    "目前市面上已经有很多大语言模型，比如OpenAI的ChatGPT、百度的文心一言等等。大语言模型通常具有大量参数，对运行机器的显存和算量有很高的要求。为了方便大家使用，这些大语言模型都有对外提供相应的调用接口。\n",
    "\n",
    "在`ERNIE Bot Agent`中，我们支持快速调用文心一言的多个模型，包括`ernie-3.5`、`ernie-turbo`、`ernie-4.0`和`ernie-3.5-8k`。\n",
    "\n",
    "| 模型名称 | 说明 | 功能 | 输入token数量上限 |\n",
    "|:--- | :--- | :--- | :--- |\n",
    "| ernie-3.5 | 文心大模型3.5版本。具备优秀的知识增强和内容生成能力，在文本创作、问答、推理和代码生成等方面表现出色。 | 对话补全，函数调用 | 3000 |\n",
    "| ernie-turbo | 文心大模型。相比ernie-3.5模型具备更快的响应速度和学习能力，API调用成本更低。 | 对话补全 |  3000 |\n",
    "| ernie-4.0 | 文心大模型4.0版本，具备目前系列模型中最优的理解和生成能力。 | 对话补全，函数调用 |  3000 |\n",
    "| ernie-3.5-8k | 文心大模型。在ernie-3.5模型的基础上增强了对长对话上下文的支持，输入token数量上限为7000。 | 对话补全，函数调用 |  7000 |"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 上手使用文心一言\n",
    "\n",
    "### 安装\n",
    "\n",
    "大家可以参考[官方文档](https://github.com/PaddlePaddle/ERNIE-SDK)，进行安装ERNIE Bot Agent 和 ERNIE Bot。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 鉴权\n",
    "\n",
    "大家在使用ERNIE Bot Agent之前，需要完成鉴权步骤：\n",
    "\n",
    "* 在[AI Studio星河社区](https://aistudio.baidu.com/index)注册并登录账号\n",
    "* 在个人中心的[访问令牌页面](https://aistudio.baidu.com/index/accessToken)获取用户凭证`Access Token`\n",
    "* 通过环境变量或者`Python`代码设置`Access Token`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2023-12-28T13:03:44.287064Z",
     "iopub.status.busy": "2023-12-28T13:03:44.286552Z",
     "iopub.status.idle": "2023-12-28T13:03:44.292499Z",
     "shell.execute_reply": "2023-12-28T13:03:44.291206Z",
     "shell.execute_reply.started": "2023-12-28T13:03:44.287031Z"
    },
    "scrolled": true,
    "tags": []
   },
   "outputs": [],
   "source": [
    "# %env EB_AGENT_ACCESS_TOKEN=xxxxxxxx\n",
    "\n",
    "import os\n",
    "\n",
    "os.environ[\"EB_AGENT_ACCESS_TOKEN\"] = \"xxxxxxxx\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 使用文心一言\n",
    "\n",
    "首先，导入必要的依赖库。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2023-12-28T13:03:44.294826Z",
     "iopub.status.busy": "2023-12-28T13:03:44.294448Z",
     "iopub.status.idle": "2023-12-28T13:03:44.299186Z",
     "shell.execute_reply": "2023-12-28T13:03:44.298453Z",
     "shell.execute_reply.started": "2023-12-28T13:03:44.294799Z"
    },
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "import json\n",
    "import asyncio\n",
    "from erniebot_agent.chat_models import ERNIEBot\n",
    "from erniebot_agent.memory import HumanMessage, AIMessage, SystemMessage, FunctionMessage"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "出于使用场景和性能的考虑，`ERNIE Bot Agent`只提供异步接口来调用文心一言模型。关于异步接口的详细介绍，请参考[asyncio文档](https://docs.python.org/3/library/asyncio.html)。\n",
    "\n",
    "如下示例，我们首先创建文心一言`ernie-3.5`模型，然后两次调用`chat`接口传入只有单条`HumanMessage`的数组，文心一言模型会对单条`HumanMessage`做出回答，返回一条`AIMessage`。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2023-12-28T13:03:44.300791Z",
     "iopub.status.busy": "2023-12-28T13:03:44.300211Z",
     "iopub.status.idle": "2023-12-28T13:04:04.402653Z",
     "shell.execute_reply": "2023-12-28T13:04:04.401694Z",
     "shell.execute_reply.started": "2023-12-28T13:03:44.300764Z"
    },
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "你好，我是文心一言，英文名是ERNIE Bot，可以协助你完成范围广泛的任务并提供有关各种主题的信息，比如回答问题，提供定义和解释及建议。如果你有任何问题，请随时向我提问。 \r\n",
      "\r\n",
      "深圳是中国的一座经济特区，也是中国最现代化的城市之一，有许多值得游览的景点。以下是三个深圳有名的景点：\r\n",
      "\r\n",
      "1. 深圳欢乐谷：深圳欢乐谷是一个主题公园，拥有各种现代化的游乐设施和表演节目，适合家庭游玩。这里有许多惊险刺激的过山车、碰碰车等项目，也有适合儿童玩的儿童区。此外，还有各种主题活动和节日庆典，如万圣节鬼屋、圣诞节狂欢等。\r\n",
      "2. 深圳世界之窗：深圳世界之窗是一个微缩景观公园，展示了世界各地的著名建筑和景点，如埃菲尔铁塔、比萨斜塔、金字塔等。游客可以在这里欣赏到世界各地的文化和建筑风格，同时也可以参观各种主题展览和表演。\r\n",
      "3. 深圳东部华侨城：深圳东部华侨城是一个大型旅游度假区，包括大峡谷、茶溪谷、云海谷等多个主题区。这里有各种刺激的游乐设施、主题酒店、购物中心等，是一个适合全家游玩的综合性度假胜地。\r\n",
      "\r\n",
      "以上三个景点各有特色，为游客提供了不同的游玩体验，是深圳旅游的重要景点。"
     ]
    }
   ],
   "source": [
    "model = ERNIEBot(model=\"ernie-3.5\")  # 创建模型\n",
    "human_message = HumanMessage(content=\"你好，你是谁\")  # 定义输入信息\n",
    "ai_message = await model.chat(messages=[human_message])  # 调用模型chat接口，非流式返回\n",
    "print(ai_message.content, \"\\n\")  # 输出结果\n",
    "\n",
    "human_message = HumanMessage(content=\"推荐三个深圳有名的景点\")  # 定义输入信息\n",
    "ai_message = await model.chat(messages=[human_message], stream=True)  # 调用模型chat接口，流式返回\n",
    "async for chunk in ai_message:  # 流式输出结果\n",
    "    print(chunk.content, end=\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果希望文心一言模型能够根据多轮对话的上下文进行回答，我们需要将前面对话的输入输出`Message`带入后面对话，具体参考如下代码。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2023-12-28T13:04:04.404077Z",
     "iopub.status.busy": "2023-12-28T13:04:04.403813Z",
     "iopub.status.idle": "2023-12-28T13:04:33.803704Z",
     "shell.execute_reply": "2023-12-28T13:04:33.802718Z",
     "shell.execute_reply.started": "2023-12-28T13:04:04.404053Z"
    },
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "深圳是一个现代化的城市，有许多值得一游的景点。以下是三个深圳有名的景点推荐：\r\n",
      "\r\n",
      "1. 深圳欢乐谷：这是一个大型的主题公园，拥有各种刺激和好玩的游乐设施，适合家庭游玩。此外，这里还有各种表演和活动，可以让人感受到浓郁的文化氛围。\r\n",
      "2. 深圳东部华侨城：这是一个综合性的旅游度假区，拥有主题公园、温泉、高尔夫球场等设施，适合休闲和放松。这里的环境优美，让人可以远离城市的喧嚣，享受大自然的美妙。\r\n",
      "3. 深圳海洋世界：这是一个展示海洋生物和海洋知识的主题公园，拥有各种海洋生物和水上活动，适合家庭和儿童游玩。这里不仅可以让人了解海洋知识，还可以让人感受到海洋的神奇和美妙。\r\n",
      "\r\n",
      "这些景点都是深圳比较有名的旅游景点，各有特色，让人可以全面了解深圳的文化和历史，感受这个城市的魅力和活力。 \r\n",
      "\r\n",
      "当然，以下是一份基于你推荐的景点的一日游攻略：\r\n",
      "\r\n",
      "早上：\r\n",
      "\r\n",
      "1. 早上8点左右出发，前往深圳欢乐谷。这里是中国主题公园的领军者，拥有大量的游乐设施，适合各个年龄段的人游玩。可以先游玩一些比较刺激的项目，如过山车、激流勇进等，然后看一场精彩的表演。\r\n",
      "2. 中午：在欢乐谷内的餐厅用餐，品尝一些特色的美食。\r\n",
      "\r\n",
      "下午：\r\n",
      "\r\n",
      "1. 下午1点左右离开欢乐谷，前往深圳东部华侨城。这是一个综合性的旅游度假区，拥有优美的环境和各种休闲设施。可以先参观主题公园，了解一些海洋知识，然后去温泉放松一下身心。\r\n",
      "2. 下午4点左右离开东部华侨城，前往深圳海洋世界。这是一个以海洋为主题的公园，可以近距离观察各种海洋生物，了解海洋的奥秘。\r\n",
      "\r\n",
      "晚上：\r\n",
      "\r\n",
      "1. 晚上6点左右离开海洋世界，去深圳的商业区逛逛，体验深圳的夜生活。这里有许多购物中心和美食街，可以购物和品尝美食。\r\n",
      "2. 晚上8点左右返回酒店休息，结束一天的行程。\r\n",
      "\r\n",
      "以上是一份参考的一日游攻略，具体的行程可以根据自己的兴趣和时间进行调整。 \r\n",
      "\r\n"
     ]
    }
   ],
   "source": [
    "model = ERNIEBot(model=\"ernie-3.5\")\n",
    "messages = []  # 使用列表保存所有Message信息\n",
    "\n",
    "messages.append(HumanMessage(content=\"推荐三个深圳有名的景点\"))\n",
    "ai_message = await model.chat(messages=messages)\n",
    "messages.append(ai_message)\n",
    "print(ai_message.content, \"\\n\")\n",
    "\n",
    "messages.append(HumanMessage(content=\"根据你推荐的景点，帮我做一份一日游的攻略\"))\n",
    "ai_message = await model.chat(messages=messages)\n",
    "messages.append(ai_message)\n",
    "print(ai_message.content, \"\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下面示例，我们实现了一个简易的命令行聊天应用，可以和大语言模型网页端一样进行无限畅聊。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2023-12-28T13:04:33.805771Z",
     "iopub.status.busy": "2023-12-28T13:04:33.805033Z",
     "iopub.status.idle": "2023-12-28T13:05:38.874593Z",
     "shell.execute_reply": "2023-12-28T13:05:38.873567Z",
     "shell.execute_reply.started": "2023-12-28T13:04:33.805740Z"
    },
    "scrolled": true,
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "你好，有什么我可以帮助你的吗？ (输入q可以退出聊天)\r\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " 广东省的省会是什么\r\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "广东省的省会是**广州市**。广州市简称“穗”，别称羊城、花城，是广东省辖地级市、广东省省会、副省级市、国家中心城市、超大城市、广州都市圈核心城市，国务院批复确定的中国重要的中心城市、国际商贸中心和综合交通枢纽。\r\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " 这个城市简称什么\r\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "这个城市简称**穗**。\r\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " q\r\n"
     ]
    }
   ],
   "source": [
    "model = ERNIEBot(model=\"ernie-3.5\")\n",
    "messages = []\n",
    "\n",
    "print(\"你好，有什么我可以帮助你的吗？ (输入q可以退出聊天)\")\n",
    "while True:\n",
    "    prompt = input()\n",
    "    if prompt == \"q\":\n",
    "        break\n",
    "\n",
    "    messages.append(HumanMessage(prompt))\n",
    "    ai_message = await model.chat(messages=messages, stream=True)\n",
    "\n",
    "    result = \"\"\n",
    "    async for chunk in ai_message:\n",
    "        result += chunk.content\n",
    "        print(chunk.content, end=\"\")\n",
    "    print(\"\")\n",
    "    messages.append(AIMessage(result))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下面示例，我们展示了文心一言模型如何使用FunctionCall。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2023-12-28T13:05:38.876514Z",
     "iopub.status.busy": "2023-12-28T13:05:38.875759Z",
     "iopub.status.idle": "2023-12-28T13:05:46.773591Z",
     "shell.execute_reply": "2023-12-28T13:05:46.772576Z",
     "shell.execute_reply.started": "2023-12-28T13:05:38.876485Z"
    },
    "scrolled": true,
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "function_call: {'name': 'get_student_score', 'thoughts': '用户想要查询小明的分数，我需要使用查询学生分数的工具。', 'arguments': '{\"name\":\"小明\"}'}\r\n",
      "function result: {'score': 80}\r\n",
      "final result: 根据我的查询，小明的分数是80分。如果你有其他关于小明的成绩或者其他方面的问题，请随时告诉我。\r\n"
     ]
    }
   ],
   "source": [
    "# 定义function函数\n",
    "def get_student_score(name: str) -> dict:\n",
    "    info = {\"小明\": 80, \"小红\": 90, \"小天\": 95}\n",
    "    if name in info:\n",
    "        return {\"score\": info[name]}\n",
    "    else:\n",
    "        return f\"we do not know the score of {name}\"\n",
    "\n",
    "\n",
    "# 定义function描述\n",
    "functions = [\n",
    "    {\n",
    "        \"name\": \"get_student_score\",\n",
    "        \"description\": \"查询学生的分数\",\n",
    "        \"parameters\": {\n",
    "            \"type\": \"object\",\n",
    "            \"properties\": {\n",
    "                \"name\": {\n",
    "                    \"type\": \"string\",\n",
    "                    \"description\": \"学生姓名\",\n",
    "                },\n",
    "            },\n",
    "            \"required\": [\n",
    "                \"name\",\n",
    "            ],\n",
    "        },\n",
    "        \"responses\": {\n",
    "            \"type\": \"object\",\n",
    "            \"properties\": {\n",
    "                \"score\": {\n",
    "                    \"type\": \"integer\",\n",
    "                    \"description\": \"分数\",\n",
    "                },\n",
    "            },\n",
    "        },\n",
    "    },\n",
    "]\n",
    "\n",
    "\n",
    "model = ERNIEBot(model=\"ernie-3.5\")\n",
    "messages = []\n",
    "\n",
    "messages.append(HumanMessage(\"请问小明的分数是多少\"))\n",
    "ai_message = await model.chat(messages=messages, functions=functions)  # 发送问题，带上function描述\n",
    "messages.append(ai_message)\n",
    "\n",
    "function_call = ai_message.function_call\n",
    "if function_call is not None:  # 如果返回的AIMessage有触发function，会有function_call字段\n",
    "    name = function_call[\"name\"]  # function_call的函数名称\n",
    "    arguments = eval(function_call[\"arguments\"])  # function_call的函数输入实参\n",
    "    result = eval(name)(**arguments)  # 使用函数实参，调用并执行函数，拿到结果\n",
    "    print(\"function_call:\", function_call)\n",
    "    print(\"function result:\", result)\n",
    "\n",
    "    function_message = FunctionMessage(\n",
    "        name=name, content=json.dumps(result, ensure_ascii=False)\n",
    "    )  # 构建FunctionMessage，封装函数的结果\n",
    "    messages.append(function_message)\n",
    "\n",
    "    ai_message = await model.chat(messages=messages, functions=functions)  # 将函数结果返回给模型，进行润色，得到最终输出\n",
    "    print(\"final result:\", ai_message.content)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "py35-paddle1.2.0"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
